#include "SeedTrigger.h"

SeedTrigger g_pSeedTrigger;

void SeedTrigger::Init()
{
	Active = true;
	Shoot = false;
	DesiredCmdNum = 0;
	DesiredSeed = 0;
	int cnt = 0;
	memset(CmdNums, 0xFFFFFFFF, sizeof(DWORD) * 255);

	for(DWORD i = 0; i < 0xFFFFFFFF; ++i)
	{
		UINT seed = MD5_PseudoRandom(i) % 255;
		if(CmdNums[0][seed] == 0xFFFFFFFF)
		{
			cnt++;
			CmdNums[0][seed] = i;
			CmdNums[1][seed] = MD5_PseudoRandom(i) & 0x7FFFFFFF;
			if(cnt >= 255)
				break;
		}
	}
}

float SeedTrigger::RandomFloat(float min, float max)
{
	typedef float (* RandomFloat_t)(float,float);
	static RandomFloat_t m_RandomFloat = (RandomFloat_t)GetProcAddress(GetModuleHandle(L"vstdlib.dll"), "RandomFloat");
	return m_RandomFloat(min,max);
}

void SeedTrigger::RandomSeed(UINT seed)
{
	typedef void (* RandomSeed_t)(UINT);
	static RandomSeed_t m_RandomSeed = (RandomSeed_t)GetProcAddress(GetModuleHandle(L"vstdlib.dll"), "RandomSeed");
	m_RandomSeed(seed);
	return;
}

void SeedTrigger::GetSpreadXY(UINT seed, SD_Tools::Vector & qAngle)
{
	seed = seed & 255;
	seed++;
	RandomSeed(seed);

	Entity* pLocal = g_pInterfaces.g_pEntList->GetClientEntity(g_pInterfaces.g_pEngine->GetLocalPlayer());
	if(!pLocal)
		return;

	CBaseCombatWeapon* pWeapon = GameUtilities::GetBaseCombatWeapon(pLocal->GetBaseEntity());
	if (!pWeapon)
		return;

	//pWeapon->UpdateAccuracyPenalty();

	float Spread[2];
	Spread[0] = pWeapon->GetSpread();
	Spread[1] = pWeapon->GetInaccuracy();

	float Randoms[4];
	Randoms[0] = RandomFloat(0.0f, 2.0f * SD_Tools::SD_PI_F);
	Randoms[1] = RandomFloat(0.0f, Spread[0]);
	Randoms[2] = RandomFloat(0.0f, 2.0f * SD_Tools::SD_PI_F);
	Randoms[3] = RandomFloat(0.0f, Spread[1]);

	qAngle.x = (cos(Randoms[0]) * Randoms[1]) + (cos(Randoms[2]) * Randoms[3]);
	qAngle.y = (sin(Randoms[0]) * Randoms[1]) + (sin(Randoms[2]) * Randoms[3]);
}

void SeedTrigger::GetSpreadFix( UINT Seed, SD_Tools::Angle &vIn, SD_Tools::Angle* vOut )
{
	SD_Tools::Vector vF,vR,vU,vDir;
	SD_Tools::Vector View,Spread;
	SD_Tools::Angle Dest;

	SD_Tools::MathHelper::AngleToVectors(vIn,&vF,&vR,&vU);

	GetSpreadXY(Seed,Spread);

	vDir.x = vF.x + Spread.x * vR.x + Spread.y * vU.x;
	View.x = 8192.0f * vDir.x;
	vDir.y = vF.y + Spread.x * vR.y + Spread.y * vU.y;
	View.y = 8192.0f * vDir.y;
	vDir.z = vF.z + Spread.x * vR.z + Spread.y * vU.z;
	View.z = 8192.0f * vDir.z;

	View.ToAngles(&Dest);

	vOut->x = vIn.x - Dest.x;
	vOut->y = vIn.y - Dest.y;
	vOut->z = 0;
}

struct HeadBoxStruct
{
	SD_Tools::Vector DesiredVec;
	float Range;
};

bool SeedTrigger::DoTrigger(CUserCmd* cmd)
{
	static HeadBoxStruct HeadBoxes[64];
	int NumberOfValids = 0;
	SD_Tools::Vector vMin, vMax, vecPosition, vPlayer;
	SD_Tools::Angle angles;
	SD_Tools::Angle TempAngle;
	int LocalPlayerIndex = g_pInterfaces.g_pEngine->GetLocalPlayer();
	Entity* pMe = g_pInterfaces.g_pEntList->GetClientEntity(LocalPlayerIndex);

	if(!pMe)
		return false;

	CBaseCombatWeapon* Weapon = GameUtilities::GetBaseCombatWeapon(pMe);

	if (!Weapon || !Weapon->IsReady())
		return false;

	SD_Tools::Vector m_Eye = pMe->GetEyePosition();

	GameUtilities::Predict(pMe, m_Eye, 1.f);

	SD_Tools::Vector punch = *pMe->getRecoilVec();
	SD_Tools::Angle fTraceVec;


	int MaxEntities = g_pInterfaces.g_pEntList->GetHighestEntityIndex();
	player_info_t pinfo;
	SD_Tools::Vector RealHead;
	SD_Tools::Vector CurrentVec;
	SD_Tools::Vector DesiredVec;

	for(int i = 1; i <= MaxEntities && NumberOfValids < 64; i++)
	{
		Entity* baseEntity = g_pInterfaces.g_pEntList->GetClientEntity(i);

		if(i == LocalPlayerIndex || baseEntity == NULL || *baseEntity->dormant() || baseEntity == pMe || *baseEntity->lifeState() != 0 /*LIFE_ALIVE*/ || !g_pInterfaces.g_pEngine->GetPlayerInfo(i, &pinfo) || *baseEntity->iTeamNum() == *pMe->iTeamNum() || !baseEntity->GetMidHead(&RealHead) || !GameUtilities::IsVisible(m_Eye, RealHead, pMe, baseEntity))
		{
			continue;
		}

		float Range = GameUtilities::GetMaxRange(m_Eye, RealHead, 4.0f);
		DesiredVec = SD_Tools::MathHelper::SubVector(m_Eye, RealHead);

		HeadBoxes[NumberOfValids].DesiredVec = DesiredVec;
		HeadBoxes[NumberOfValids].Range = Range;
		NumberOfValids++;
	}

	Trace_t tr;
	Ray_t ray;
	SD_Tools::Vector Dir, Origin, Forward, Right, Up;

	for(DWORD i = 0; i < 255; ++i)
	{
		DWORD seed = CmdNums[1][i];

		fTraceVec = cmd->viewangles;

		GetSpreadFix(seed, fTraceVec, &TempAngle);

		fTraceVec.Subtract(TempAngle);

		fTraceVec.x += punch.x*1.92f;
		fTraceVec.y += punch.y*1.92f;

		SD_Tools::MathHelper::AngleToVectors(fTraceVec, &Forward, &Right, &Up);

		Origin.x = Forward.x * 8192.0f + m_Eye.x;
		Origin.y = Forward.y * 8192.0f + m_Eye.y;
		Origin.z = Forward.z * 8192.0f + m_Eye.z;

		CurrentVec = SD_Tools::MathHelper::SubVector(Origin, m_Eye);

		for (int j = 0; j < NumberOfValids; j++)
		{
			float CurRange = GameUtilities::GetAngleBetween(CurrentVec, HeadBoxes[j].DesiredVec);

			if (180.0f - CurRange < HeadBoxes[j].Range)
			{
				DesiredCmdNum = CmdNums[0][i];
				DesiredSeed = seed;
				return true;
			}
		}

		/*ray.Init(m_Eye, Origin);

		g_pInterfaces.g_pEngineTrace->TraceRay(ray, MASK_HITBOX, NULL, &tr);

		if(!tr.m_pEnt || *tr.m_pEnt->dormant()  || *tr.m_pEnt->iTeamNum() == *pMe->iTeamNum() || !g_pInterfaces.g_pEngine->GetPlayerInfo(tr.m_pEnt->GetIndex(), &pInfo) || tr.hitbox != 12)
			continue;*/



		/*DesiredCmdNum = CmdNums[i];
		DesiredSeed = seed;
		return true;*/
	}
	return false;
}

void SeedTrigger::Compute( CUserCmd* cmd )
{
	if(!GameUtilities::HasGun())
		return;
	static bool LastDecision;
	static int LastCmdNum;
	if (Active)
	{
		if (LastCmdNum == cmd->command_number)
		{
			Shoot = LastDecision;
			return;
		}
		LastDecision = false;
		LastCmdNum = cmd->command_number;
		Entity* m_localEntity = g_pInterfaces.g_pEntList->GetClientEntity(g_pInterfaces.g_pEngine->GetLocalPlayer());
		if(m_localEntity)
		{
			if(DoTrigger(cmd))
			{
				if(GameUtilities::HasGun())
				{
					Shoot = true;
					LastDecision = true;
				}
			}
		}
	}
}

void SeedTrigger::Enable()
{
	Active = true;
}

void SeedTrigger::Disable()
{
	Active = false;
}

void SeedTrigger::Set( bool Active )
{
	this->Active = Active;
}

void SeedTrigger::Action( CUserCmd* cmd )
{
	if (Shoot)
	{
		cmd->random_seed = DesiredSeed;
		cmd->command_number = DesiredCmdNum;
		cmd->buttons |= IN_ATTACK;
		Shoot = false;
	}
}
